package cn.good.yan.service.t5;

import cn.good.yan.service.t5.pojo.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 缓存穿透 - 初始化 把数据存在Redis中
 *
 * @author shengren.yan
 * @create 2023-11-28
 */
@Service
@Slf4j
public class CtService {

    public static final String JHS_KEY = "jhs";
    public static final String JHS_KEY_A = "jhs:a";
    public static final String JHS_KEY_B = "jhs:b";

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 模拟从数据库读取20件特价商品 - 模拟
     * @return
     */
    private List<Product> getProductsFromMysql() {
        List<Product> list = new ArrayList<>();
        for (int i = 0; i <= 20; i++) {
            Random random = new Random();
            int id = random.nextInt(10000);
            Product product = new Product((long) id, "product" + i, i, "detail");
            list.add(product);
        }
        return list;
    }

    // @PostConstruct   // 初始化 把mysql里的数据加载到redis，这里有先删除缓存，再更新到缓存中
    public void initA(){
        log.info("启动定时器 模拟开始 ===============");
        // 1.用线程模拟定时任务，后台任务定时将mysql里面的特价商品刷新的redis
        new Thread(() -> {
            while (true){
                // 2.模拟从mysql查到数据，加到redis并返回给页面
                List<Product> list = this.getProductsFromMysql();
                // 3.采用redis list数据结构的lpush命令来实现存储
                redisTemplate.delete(JHS_KEY);
                // 4.加入最新的数据给redis
                redisTemplate.opsForList().leftPushAll(JHS_KEY, list);
                // 5.暂停1分钟，间隔1分钟执行一次，模拟聚划算一天执行的参加活动的品牌
                try {
                    TimeUnit.MINUTES.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t1线程").start();

    }

    /**
     * 差异失效时间   // 初始化 把mysql里的数据加载到redis，这里有先删除缓存B，更新缓存B，再删除缓存A，更新缓存A
     */
    @PostConstruct   // 测试双缓存
    public void initB(){
        log.info("启动定时器 模拟开始 ===============");
        // 1.用线程模拟定时任务，后台任务定时将mysql里面的特价商品刷新的redis
        new Thread(() -> {
            while (true){
                // 2.模拟从mysql查到数据，加到redis并返回给页面
                List<Product> list = this.getProductsFromMysql();
                // 3.先更新B缓存且让B缓存过期时间超过缓存A时间，如果A突然失效了还有B兜底，防止击穿
                redisTemplate.delete(JHS_KEY_B);
                redisTemplate.opsForList().leftPushAll(JHS_KEY_B, list);
                redisTemplate.expire(JHS_KEY_B, 86410L, TimeUnit.SECONDS);
                // 4.再更新A缓存
                redisTemplate.delete(JHS_KEY_A);
                redisTemplate.opsForList().leftPushAll(JHS_KEY_A, list);
                redisTemplate.expire(JHS_KEY_A, 86400L, TimeUnit.SECONDS);
                // 5.暂停1分钟，间隔1分钟执行一次，模拟聚划算一天执行的参加活动的品牌
                try {
                    TimeUnit.MINUTES.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t1线程").start();
    }


}
